#pragma once
#include <string>
#include "../../DbType.h"
#include "SQLParserFeature.h"
#include "SQLStatementParser.h"
#include "SQLExprParser.h"

class SQLParserUtils
{
public:
  class SimpleValueEvalHandler
  {
    Object_ptr eval(SQLExpr_ptr expr);
  };

  static SQLStatementParser_ptr createSQLStatementParser(string_ptr sql, DbType_ptr dbType);
  static SQLStatementParser_ptr createSQLStatementParser(string_ptr sql, DbType_ptr dbType, BOOL_ptr keepComments);
  static SQLStatementParser_ptr createSQLStatementParser(string_ptr sql, DbType_ptr dbType, SQLParserFeature_list_ptr features);

  static SQLExprParser_ptr createExprParser(string_ptr sql, DbType_ptr dbType);
  static SQLExprParser_ptr createExprParser(string_ptr sql, DbType_ptr dbType, SQLParserFeature_list_ptr features);

  static Lexer_ptr createLexer(string_ptr sql, DbType_ptr dbType);

  static Lexer_ptr createLexer(string_ptr sql, DbType_ptr dbType, SQLParserFeature_list_ptr features);

  static SQLSelectQueryBlock *createSelectQueryBlock(DbType_ptr dbType);

  // static SQLType* getSQLType(string_ptr  sql, DbType dbType) {
  //     Lexer_ptr  lexer = createLexer(sql, dbType);
  //     return lexer.scanSQLType();
  // }

  // static SQLType* getSQLTypeV2(string_ptr  sql, DbType dbType) {
  //     Lexer_ptr  lexer = createLexer(sql, dbType);
  //     return lexer.scanSQLTypeV2();
  // }

  // static BOOL_ptr startsWithHint(string_ptr  sql, DbType dbType) {
  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.nextToken();
  //     return lexer.token() == Token.HINT;
  // }

  // static BOOL_ptr containsAny(string_ptr  sql, DbType dbType, Token token) {
  //     Lexer lexer = createLexer(sql, dbType);
  //     for (; ; ) {
  //         lexer.nextToken();
  //         final Token tok = lexer.token;
  //         switch (tok) {
  //             case XEOF:
  //             case ERROR:
  //                 return BOOL::FALSE;
  //             default:
  //                 if (tok == token) {
  //                     return BOOL::TRUE;
  //                 }
  //                 break;
  //         }
  //     }
  // }

  // static BOOL_ptr containsAny(string_ptr  sql, DbType dbType, Token token1, Token token2) {
  //     Lexer lexer = createLexer(sql, dbType);
  //     for (; ; ) {
  //         lexer.nextToken();
  //         final Token tok = lexer.token;
  //         switch (tok) {
  //             case XEOF:
  //             case ERROR:
  //                 return BOOL::FALSE;
  //             default:
  //                 if (tok == token1 || tok == token2) {
  //                     return BOOL::TRUE;
  //                 }
  //                 break;
  //         }
  //     }
  // }

  // static BOOL_ptr containsAny(string_ptr  sql, DbType dbType, Token token1, Token token2, Token token3) {
  //     Lexer lexer = createLexer(sql, dbType);
  //     for (; ; ) {
  //         lexer.nextToken();
  //         final Token tok = lexer.token;
  //         switch (tok) {
  //             case XEOF:
  //             case ERROR:
  //                 return BOOL::FALSE;
  //             default:
  //                 if (tok == token1 || tok == token2 || tok == token3) {
  //                     return BOOL::TRUE;
  //                 }
  //                 break;
  //         }
  //     }
  // }

  // static BOOL_ptr containsAny(string_ptr  sql, DbType dbType, std::list<Token> tokens) {
  //     if (tokens == NULL) {
  //         return BOOL::FALSE;
  //     }

  //     Lexer lexer = createLexer(sql, dbType);
  //     for (; ; ) {
  //         lexer.nextToken();
  //         final Token tok = lexer.token;
  //         switch (tok) {
  //             case XEOF:
  //             case ERROR:
  //                 return BOOL::FALSE;
  //             default:
  //                 for (int i = 0; i < tokens.length; i++) {
  //                     if (tokens[i] == tok) {
  //                         return BOOL::TRUE;
  //                     }
  //                 }
  //                 break;
  //         }
  //     }
  // }

  // static Object_ptr  getSimpleSelectValue(string_ptr  sql, DbType dbType) ;

  // static Object_ptr  getSimpleSelectValue(string_ptr  sql, DbType dbType, SimpleValueEvalHandler handler) {
  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.nextToken();

  //     if (lexer.token != Token.SELECT && lexer.token != Token.VALUES) {
  //         return NULL;
  //     }

  //     lexer.nextTokenValue();

  //     SQLExpr_ptr expr = NULL;
  //     Object_ptr  value;
  //     switch (lexer.token) {
  //         case LITERAL_INT:
  //             value = lexer.integerValue();
  //             break;
  //         case LITERAL_CHARS:
  //         case LITERAL_NCHARS:
  //             value = lexer.stringVal();
  //             break;
  //         case LITERAL_FLOAT:
  //             value = lexer.decimalValue();
  //             break;
  //         default:
  //             if (handler == NULL) {
  //                 return NULL;
  //             }

  //             expr = new SQLExprParser(lexer).expr();
  //             try {
  //                 value = handler.eval(expr);
  //             } catch (Exception error) {
  //                 // skip
  //                 value = NULL;
  //             }
  //             break;
  //     }

  //     lexer.nextToken();

  //     if (lexer.token == Token.FROM) {
  //         lexer.nextToken();
  //         if (lexer.token == Token.DUAL) {
  //             lexer.nextToken();
  //         } else {
  //             return NULL;
  //         }
  //     }
  //     if (lexer.token != Token.XEOF) {
  //         return NULL;
  //     }

  //     return value;
  // }

  // static string_ptr  replaceBackQuote(string_ptr  sql, DbType dbType) {
  //     int i = sql.indexOf('`');

  //     if (i == -1) {
  //         return sql;
  //     }

  //     char[] chars = sql.toCharArray();
  //     Lexer lexer = SQLParserUtils.createLexer(sql, dbType);

  //     int len = chars.length;
  //     int off = 0;

  //     for_:
  //     for (; ; ) {
  //         lexer.nextToken();

  //         int p0, p1;
  //         char c0, c1;
  //         switch (lexer.token) {
  //             case IDENTIFIER:
  //                 p0 = lexer.startPos + off;
  //                 p1 = lexer.pos - 1 + off;
  //                 c0 = chars[p0];
  //                 c1 = chars[p1];
  //                 if (c0 == '`' && c1 == '`') {
  //                     if (p1 - p0 > 2 && chars[p0 + 1] == '\'' && chars[p1 - 1] == '\'') {
  //                         System.arraycopy(chars, p0 + 1, chars, p0, p1 - p0 - 1);
  //                         System.arraycopy(chars, p1 + 1, chars, p1 - 1, chars.length - p1 - 1);
  //                         len -= 2;
  //                         off -= 2;
  //                     } else {
  //                         chars[p0] = '"';
  //                         chars[p1] = '"';
  //                     }

  //                 }
  //                 break;
  //             case XEOF:
  //             case ERROR:
  //                 break for_;
  //             default:
  //                 break;
  //         }
  //     }

  //     return new string_ptr (chars, 0, len);
  // }

  // static string_ptr  addBackQuote(string_ptr  sql, DbType dbType) ;

  // static std::list<string_ptr > split(string_ptr  sql, DbType dbType) {
  //     if (dbType == NULL) {
  //         dbType = DbType::other;
  //     }

  //     {
  //         Lexer lexer = createLexer(sql, dbType);
  //         lexer.nextToken();

  //         BOOL_ptr script = BOOL::FALSE;
  //         if (dbType == DbType::odps && lexer.token == Token.VARIANT) {
  //             script = BOOL::TRUE;
  //         }

  //         if (script) {
  //             return Collections.singletonList(sql);
  //         }
  //     }

  //     std::list list = new ArrayList();

  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //     lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //     BOOL_ptr set = BOOL::FALSE, paiOrJar = BOOL::FALSE;
  //     int start = 0;
  //     Token token = lexer.token;
  //     for (; lexer.token != Token.XEOF; ) {
  //         if (token == Token.SEMI) {
  //             int len = lexer.startPos - start;
  //             if (len > 0) {
  //                 string_ptr  lineSql = sql.substring(start, lexer.startPos);
  //                 lineSql = lineSql.trim();
  //                 if (!lineSql.isEmpty()) {
  //                     list.add(lineSql);
  //                 }
  //             }
  //             start = lexer.startPos + 1;
  //             set = BOOL::FALSE;
  //         } else if (token == Token.CREATE) {
  //             lexer.nextToken();

  //             if (lexer.token == Token.FUNCTION || lexer.identifierEquals("FUNCTION")) {
  //                 lexer.nextToken();
  //                 lexer.nextToken();
  //                 if (lexer.token == Token.AS) {
  //                     lexer.nextToken();
  //                     if (lexer.token == Token.LITERAL_CHARS) {
  //                         lexer.nextToken();
  //                         token = lexer.token;
  //                         continue;
  //                     }
  //                 }
  //                 lexer.startPos = sql.length();
  //                 break;
  //             }

  //             token = lexer.token;
  //             continue;
  //         } else if (set && token == Token.EQ && dbType == DbType::odps) {
  //             lexer.nextTokenForSet();
  //             token = lexer.token;
  //             continue;
  //         }

  //         if (lexer.identifierEquals("USING")) {
  //             lexer.nextToken();
  //             if (lexer.identifierEquals("jar")) {
  //                 lexer.nextToken();
  //             }
  //         }

  //         if (lexer.token == Token.SET) {
  //             set = BOOL::TRUE;
  //         }

  //         if (lexer.identifierEquals("ADD") && (dbType == DbType::hive || dbType == DbType::odps)) {
  //             lexer.nextToken();
  //             if (lexer.identifierEquals("JAR")) {
  //                 lexer.nextPath();
  //             }
  //         } else {
  //             lexer.nextToken();
  //         }
  //         token = lexer.token;
  //     }

  //     if (start != sql.length() && token != Token.SEMI) {
  //         int end = lexer.startPos;
  //         if (end > sql.length()) {
  //             end = sql.length();
  //         }
  //         string_ptr  splitSql = sql.substring(start, end).trim();
  //         if (!paiOrJar) {
  //             splitSql = removeComment(splitSql, dbType).trim();
  //         } else {
  //             if (splitSql.endsWith(";")) {
  //                 splitSql = splitSql.substring(0, splitSql.length() - 1).trim();
  //             }
  //         }
  //         if (!splitSql.isEmpty()) {
  //             list.add(splitSql);
  //         }
  //     }

  //     return list;
  // }

  // static std::list<string_ptr > splitAndRemoveComment(string_ptr  sql, DbType dbType) {
  //     if (dbType == NULL) {
  //         dbType = DbType::other;
  //     }

  //     BOOL_ptr containsCommentAndSemi = BOOL::FALSE;
  //     {
  //         Lexer lexer = createLexer(sql, dbType);
  //         lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //         lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //         while (lexer.token != Token.XEOF) {
  //             if (lexer.token == Token.LINE_COMMENT
  //                     || lexer.token == Token.MULTI_LINE_COMMENT
  //                     || lexer.token == Token.SEMI) {
  //                 containsCommentAndSemi = BOOL::TRUE;
  //                 break;
  //             }
  //             lexer.nextToken();
  //         }

  //         if (!containsCommentAndSemi) {
  //             return Collections.singletonList(sql);
  //         }
  //     }

  //     {
  //         Lexer lexer = createLexer(sql, dbType);
  //         lexer.nextToken();

  //         BOOL_ptr script = BOOL::FALSE;
  //         if (dbType == DbType::odps && lexer.token == Token.VARIANT) {
  //             script = BOOL::TRUE;
  //         }

  //         if (script || lexer.identifierEquals("pai") || lexer.identifierEquals("jar") || lexer.identifierEquals("copy")) {
  //             return Collections.singletonList(sql);
  //         }
  //     }

  //     std::list list = new ArrayList();

  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //     lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //     BOOL_ptr set = BOOL::FALSE, paiOrJar = BOOL::FALSE;
  //     int start = 0;
  //     Token preToken = NULL;
  //     int prePos = 0;
  //     Token token = lexer.token;
  //     for (; lexer.token != Token.XEOF; ) {
  //         if (token == Token.SEMI) {
  //             int len = lexer.startPos - start;
  //             if (len > 0) {
  //                 string_ptr  lineSql = sql.substring(start, lexer.startPos);
  //                 string_ptr  splitSql = set
  //                         ? removeLeftComment(lineSql, dbType)
  //                         : removeComment(lineSql, dbType
  //                 ).trim();
  //                 if (!splitSql.isEmpty()) {
  //                     list.add(splitSql);
  //                 }
  //             }
  //             start = lexer.startPos + 1;
  //             set = BOOL::FALSE;
  //         } else if (token == Token.MULTI_LINE_COMMENT) {
  //             int len = lexer.startPos - start;
  //             if (len > 0) {
  //                 string_ptr  splitSql = removeComment(
  //                         sql.substring(start, lexer.startPos),
  //                         dbType
  //                 ).trim();
  //                 if (!splitSql.isEmpty()) {
  //                     list.add(splitSql);
  //                 }
  //             }
  //             lexer.nextToken();
  //             token = lexer.token;
  //             start = lexer.startPos;
  //             continue;
  //         } else if (token == Token.CREATE) {
  //             lexer.nextToken();

  //             if (lexer.token == Token.FUNCTION || lexer.identifierEquals("FUNCTION")) {
  //                 lexer.nextToken();
  //                 lexer.nextToken();
  //                 if (lexer.token == Token.AS) {
  //                     lexer.nextToken();
  //                     if (lexer.token == Token.LITERAL_CHARS) {
  //                         lexer.nextToken();
  //                         token = lexer.token;
  //                         continue;
  //                     }
  //                 }
  //                 lexer.startPos = sql.length();
  //                 break;
  //             }

  //             token = lexer.token;
  //             continue;
  //         } else if (set && token == Token.EQ && dbType == DbType::odps) {
  //             lexer.nextTokenForSet();
  //             token = lexer.token;
  //             continue;
  //         } else if (dbType == DbType::odps
  //                 && (preToken == NULL || preToken == Token.LINE_COMMENT || preToken == Token.SEMI)
  //                 && (lexer.identifierEquals("pai") || lexer.identifierEquals("jar") || lexer.identifierEquals("copy"))) {
  //             lexer.scanLineArgument();
  //             paiOrJar = BOOL::TRUE;
  //         }

  //         if (lexer.identifierEquals("USING")) {
  //             lexer.nextToken();
  //             if (lexer.identifierEquals("jar")) {
  //                 lexer.nextToken();
  //             }
  //         }

  //         if (lexer.token == Token.SET) {
  //             set = BOOL::TRUE;
  //         }

  //         prePos = lexer.pos;
  //         if (lexer.identifierEquals("ADD") && (dbType == DbType::hive || dbType == DbType::odps)) {
  //             lexer.nextToken();
  //             if (lexer.identifierEquals("JAR")) {
  //                 lexer.nextPath();
  //             }
  //         } else {
  //             lexer.nextToken();
  //         }
  //         preToken = token;
  //         token = lexer.token;
  //         if (token == Token.LINE_COMMENT
  //                 && (preToken == Token.SEMI || preToken == Token.LINE_COMMENT || preToken == Token.MULTI_LINE_COMMENT)
  //         ) {
  //             start = lexer.pos;
  //         }
  //     }

  //     if (start != sql.length() && token != Token.SEMI) {
  //         int end = lexer.startPos;
  //         if (end > sql.length()) {
  //             end = sql.length();
  //         }
  //         string_ptr  splitSql = sql.substring(start, end).trim();
  //         if (!paiOrJar) {
  //             splitSql = removeComment(splitSql, dbType).trim();
  //         } else {
  //             if (splitSql.endsWith(";")) {
  //                 splitSql = splitSql.substring(0, splitSql.length() - 1).trim();
  //             }
  //         }
  //         if (!splitSql.isEmpty()) {
  //             list.add(splitSql);
  //         }
  //     }

  //     return list;
  // }

  // static string_ptr  removeLeftComment(string_ptr  sql, DbType dbType) {
  //     if (dbType == NULL) {
  //         dbType = DbType::other;
  //     }

  //     sql = sql.trim();
  //     if (sql.startsWith("jar")) {
  //         return sql;
  //     }

  //     BOOL_ptr containsComment = BOOL::FALSE;
  //     {
  //         Lexer lexer = createLexer(sql, dbType);
  //         lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //         lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //         while (lexer.token != Token.XEOF) {
  //             if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
  //                 containsComment = BOOL::TRUE;
  //                 break;
  //             }
  //             lexer.nextToken();
  //         }

  //         if (!containsComment) {
  //             return sql;
  //         }
  //     }

  //     StringBuilder sb = new StringBuilder();

  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //     lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);
  //     lexer.nextToken();

  //     int start = 0;
  //     for (; lexer.token != Token.XEOF; lexer.nextToken()) {
  //         if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
  //             continue;
  //         }
  //         start = lexer.startPos;
  //         break;
  //     }

  //     if (start != sql.length()) {
  //         sb.append(sql.substring(start, sql.length()));
  //     }

  //     return sb.toString();
  // }

  // static string_ptr  removeComment(string_ptr  sql, DbType dbType) {
  //     if (dbType == NULL) {
  //         dbType = DbType::other;
  //     }

  //     sql = sql.trim();
  //     if (sql.startsWith("jar")) {
  //         return sql;
  //     }

  //     if ((sql.startsWith("pai") || sql.startsWith("PAI")) && sql.indexOf(';') == -1) {
  //         return sql;
  //     }

  //     BOOL_ptr containsComment = BOOL::FALSE;
  //     {
  //         Lexer lexer = createLexer(sql, dbType);
  //         lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //         lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //         while (lexer.token != Token.XEOF) {
  //             if (lexer.token == Token.LINE_COMMENT || lexer.token == Token.MULTI_LINE_COMMENT) {
  //                 containsComment = BOOL::TRUE;
  //                 break;
  //             }
  //             lexer.nextToken();
  //         }

  //         if (!containsComment) {
  //             return sql;
  //         }
  //     }

  //     StringBuilder sb = new StringBuilder();

  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.config(SQLParserFeature.SkipComments, BOOL::FALSE);
  //     lexer.config(SQLParserFeature.KeepComments, BOOL::TRUE);

  //     int start = 0;
  //     Token token = lexer.token;
  //     for (; lexer.token != Token.XEOF; ) {
  //         if (token == Token.LINE_COMMENT) {
  //             int len = lexer.startPos - start;
  //             if (len > 0) {
  //                 sb.append(sql.substring(start, lexer.startPos));
  //             }
  //             start = lexer.startPos + lexer.stringVal().length();
  //         } else if (token == Token.MULTI_LINE_COMMENT) {
  //             int len = lexer.startPos - start;
  //             if (len > 0) {
  //                 sb.append(sql.substring(start, lexer.startPos));
  //             }
  //             start = lexer.startPos + lexer.stringVal().length();
  //         }

  //         if (lexer.identifierEquals("ADD")) {
  //             lexer.nextToken();
  //             if (lexer.identifierEquals("JAR")) {
  //                 lexer.nextPath();
  //             }
  //         } else {
  //             lexer.nextToken();
  //         }
  //         token = lexer.token;
  //     }

  //     if (start != sql.length() && token != Token.LINE_COMMENT && token != Token.MULTI_LINE_COMMENT) {
  //         sb.append(sql.substring(start, sql.length()));
  //     }

  //     return sb.toString();
  // }

  // static std::list<string_ptr > getTables(string_ptr  sql, DbType dbType) {
  //     std::set<string_ptr > tables = new LinkedHashSet<>();

  //     BOOL_ptr set = BOOL::FALSE;
  //     Lexer lexer = createLexer(sql, dbType);
  //     lexer.nextToken();

  //     SQLExprParser exprParser;
  //     switch (dbType) {
  //         case odps:
  //             exprParser = new OdpsExprParser(lexer);
  //             break;
  //         case mysql:
  //             exprParser = new MySqlExprParser(lexer);
  //             break;
  //         default:
  //             exprParser = new SQLExprParser(lexer);
  //             break;
  //     }

  //     for_:
  //     for (; lexer.token != Token.XEOF; ) {
  //         switch (lexer.token) {
  //             case CREATE:
  //             case DROP:
  //             case ALTER:
  //                 set = BOOL::FALSE;
  //                 lexer.nextToken();

  //                 if (lexer.token == Token.TABLE) {
  //                     lexer.nextToken();

  //                     if (lexer.token == Token.IF) {
  //                         lexer.nextToken();

  //                         if (lexer.token == Token.NOT) {
  //                             lexer.nextToken();
  //                         }

  //                         if (lexer.token == Token.EXISTS) {
  //                             lexer.nextToken();
  //                         }
  //                     }

  //                     SQLName name = exprParser.name();
  //                     tables.add(name.toString());

  //                     if (lexer.token == Token.AS) {
  //                         lexer.nextToken();
  //                     }
  //                 }
  //                 continue for_;
  //             case FROM:
  //             case JOIN:
  //                 lexer.nextToken();
  //                 if (lexer.token != Token.LPAREN
  //                         && lexer.token != Token.VALUES
  //                 ) {
  //                     SQLName name = exprParser.name();
  //                     tables.add(name.toString());
  //                 }
  //                 continue for_;
  //             case SEMI:
  //                 set = BOOL::FALSE;
  //                 break;
  //             case SET:
  //                 set = BOOL::TRUE;
  //                 break;
  //             case EQ:
  //                 if (set && dbType == DbType::.odps) {
  //                     lexer.nextTokenForSet();
  //                     continue for_;
  //                 }
  //                 break;
  //             default:
  //                 break;
  //         }

  //         lexer.nextToken();

  //     }

  //     return new ArrayList<>(tables);
  // }
};
